﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Globalization;


namespace PMS.Exceptions
{
    public enum LogLevel
    {
        None = 0,
        Debug = 1,
        Info = 2,
        Warning = 3,
        Error = 4,
        Fatal = 5,
        All = 6,
        ImportInfo = 7
    };
    /// <summary>
    /// Facade for writing a Write entry to one or more locations.
    /// This class is sealed.
    /// </summary>
    public class Logger
    {
        private static Int32 m_MaximumFileSize;
        private static string m_LogPath;

        private static string m_logfile_Debug;
        private static string m_logfile_Info;
        private static string m_logfile_Warning;
        private static string m_logfile_Error;
        private static string m_logfile_Fatal;

        private const string PRE_FIX_DEBUG = "QLTemMay_Log_";
        private const string PRE_FIX_INFO = "QLTiemMay_Info_";
        private const string PRE_FIX_WARNING = "QLTiemMay_Warning_";
        private const string PRE_FIX_ERROR = "QLTiemMay_Error_";
        private const string PRE_FIX_FATAL = "QLTiemMay_Faltal_";

        private const int MAX_FILE_TO_BACKUP = 10;

        private static bool[] m_isLog = new bool[] { false, false, false, false, false, false, false };

        static Logger()
        {
            string value;
            string level;
            string levelconfig;
            value = "2";
            m_MaximumFileSize = Int32.Parse(value);
            if (m_MaximumFileSize == 0)
            {
                m_MaximumFileSize = 1024; //KB
            }
            else
            {
                m_MaximumFileSize = m_MaximumFileSize * 1024;// MB --> KB
            }
            bool hasDebug = true;
            levelconfig = "all";
            if (!string.IsNullOrEmpty(levelconfig))
            {

                string[] splitLevels = levelconfig.Replace(" ", "").Split(',');
                if (splitLevels.Length > 0)
                {
                    foreach (string s in splitLevels)
                    {
                        level = s.ToLower().ToString();
                        if (!string.IsNullOrEmpty(level) && level.Trim().ToLower() == "debug")
                        {
                            m_isLog[1] = true;
                            hasDebug = false;
                        }
                        else if (!string.IsNullOrEmpty(level) && level.Trim().ToLower() == "info")
                        {
                            m_isLog[2] = true;
                            hasDebug = false;
                        }
                        else if (!string.IsNullOrEmpty(level) && level.Trim().ToLower() == "warning")
                        {
                            m_isLog[3] = true;
                            hasDebug = false;
                        }
                        else if (!string.IsNullOrEmpty(level) && level.Trim().ToLower() == "error")
                        {
                            m_isLog[4] = true;
                            hasDebug = false;
                        }
                        else if (!string.IsNullOrEmpty(level) && level.Trim().ToLower() == "fatal")
                        {
                            m_isLog[5] = true;
                            hasDebug = false;
                        }
                        else if (!string.IsNullOrEmpty(level) && level.Trim().ToLower() == "all")
                        {
                            m_isLog[6] = true;
                            hasDebug = false;
                            for (int i = 1; i < 6; i++)
                                m_isLog[i] = true;
                            break;
                        }
                    }
                }
            }

            m_isLog[0] = hasDebug;


            m_LogPath = "";
            if (!string.IsNullOrEmpty(m_LogPath))
            {
                try
                {
                    if (!Directory.Exists(m_LogPath))
                    {
                        Directory.CreateDirectory(m_LogPath);
                    }
                }
                catch
                {
                    m_LogPath = string.Empty;
                }
            }

            if (string.IsNullOrEmpty(m_LogPath))
            {
                m_LogPath = Directory.GetCurrentDirectory();
            }

            m_logfile_Debug = GetLatestLogFile(LogLevel.Debug);
            m_logfile_Info = GetLatestLogFile(LogLevel.Info);
            m_logfile_Warning = GetLatestLogFile(LogLevel.Warning);
            m_logfile_Error = GetLatestLogFile(LogLevel.Error);
            m_logfile_Fatal = GetLatestLogFile(LogLevel.Fatal);
        }


        #region Debug logging
        private static bool m_IsDebugEnabled = true;
        public static bool IsDebugEnabled
        {
            get { return m_IsDebugEnabled; }
            set { m_IsDebugEnabled = value; }
        }


        public static void ImportInfo(object message)
        {
            if ((m_isLog[2] == false) || (m_isLog[0] == true))
            {
                return;
            }
            WriteLog(LogLevel.ImportInfo, message);
        }
        public static void Debug(object message)
        {
            if (IsDebugEnabled)
            {
                if ((m_isLog[1] == false) || (m_isLog[0] == true))
                {
                    return;
                }
                WriteLog(LogLevel.Debug, message);
            }
        }

        public static void Debug(object message, Exception exception)
        {
            if (IsDebugEnabled)
            {
                if ((m_isLog[1] == false) || (m_isLog[0] == true))
                {
                    return;
                }
                WriteLog(LogLevel.Debug, message, exception);
            }
        }
        #endregion

        #region Info logging
        private static bool m_IsInfoEnabled = true;
        private static bool IsInfoEnabled
        {
            get { return m_IsInfoEnabled; }
        }

        public static void Info(object message)
        {
            if (IsInfoEnabled)
            {
                if ((m_isLog[2] == false) || (m_isLog[0] == true))
                {
                    return;
                }
                WriteLog(LogLevel.Info, message);
            }
        }

        public static void Info(object message, Exception exception)
        {
            if (IsInfoEnabled)
            {
                if ((m_isLog[2] == false) || (m_isLog[0] == true))
                {
                    return;
                }
                WriteLog(LogLevel.Info, message, exception);
            }
        }
        #endregion

        #region Warn logging
        private static bool m_IsWarnEnabled = true;
        public static bool IsWarnEnabled
        {
            get { return m_IsWarnEnabled; }
        }

        public static void Warn(object message)
        {
            if (IsWarnEnabled)
            {
                if ((m_isLog[3] == false) || (m_isLog[0] == true))
                {
                    return;
                }
                WriteLog(LogLevel.Warning, message);
            }
        }

        public static void Warn(object message, Exception exception)
        {
            if (IsWarnEnabled)
            {
                if ((m_isLog[3] == false) || (m_isLog[0] == true))
                {
                    return;
                }
                WriteLog(LogLevel.Warning, message, exception);
            }
        }
        #endregion

        #region Error logging
        private static bool m_IsErrorEnabled = true;
        public static bool IsErrorEnabled
        {
            get { return m_IsErrorEnabled; }
        }

        public static void Error(object message)
        {
            if (IsErrorEnabled)
            {
                if ((m_isLog[4] == false) || (m_isLog[0] == true))
                {
                    return;
                }
                WriteLog(LogLevel.Error, message);
            }
        }

        public static void Error(object message, Exception exception)
        {
            if (IsErrorEnabled)
            {
                if ((m_isLog[4] == false) || (m_isLog[0] == true))
                {
                    return;
                }
                WriteLog(LogLevel.Error, message, exception);
            }
        }
        #endregion

        #region Fatal logging
        private static bool m_IsFatalEnabled = true;
        public static bool IsFatalEnabled
        {
            get { return m_IsFatalEnabled; }
        }

        public static void Fatal(object message)
        {
            if (IsFatalEnabled)
            {
                if ((m_isLog[5] == false) || (m_isLog[0] == true))
                {
                    return;
                }
                WriteLog(LogLevel.Fatal, message);
            }
        }

        public static void Fatal(object message, Exception exception)
        {
            if (IsFatalEnabled)
            {
                if ((m_isLog[5] == false) || (m_isLog[0] == true))
                {
                    return;
                }
                WriteLog(LogLevel.Fatal, message, exception);
            }
        }
        #endregion

        #region private functions

        public static void OpenLogFile(LogLevel level)
        {
            var fileToOpen = GetLatestLogFile(level);
            var process = new Process();
            process.StartInfo = new ProcessStartInfo()
            {
                UseShellExecute = true,
                FileName = fileToOpen
            };
            process.Start();
        }

        private static string GetPatternFile(LogLevel level)
        {
            string result = string.Empty;
            string prefix = GetPrefix(level);
            if (!string.IsNullOrEmpty(prefix))
            {
                result = string.Format("{0}*.log", prefix);
            }
            else
            {
                result = string.Format("*.log", prefix);
            }
            return result;
        }

        private static string GetLatestLogFile(LogLevel level)
        {
            string result = string.Empty;
            string searchPattern = GetPatternFile(level);
            if (string.IsNullOrEmpty(searchPattern))
                return result;

            DirectoryInfo di = new DirectoryInfo(m_LogPath);

            FileInfo[] files =
                di.GetFiles(searchPattern, SearchOption.TopDirectoryOnly);

            if (files.Length > 0)
            {
                Array.Sort(files, new FileInfoComparer());
                result = files[0].FullName;
            }
            if (files.Length > MAX_FILE_TO_BACKUP)
            {
                try
                {
                    int numfiles = files.Length;
                    string backupPath = Path.Combine(m_LogPath, @"Backup");
                    backupPath = Path.Combine(backupPath, string.Format("{0}Log", GetString(level)));
                    string sourcefile;
                    string desfile;
                    if (!Directory.Exists(backupPath))
                        Directory.CreateDirectory(backupPath);
                    for (int i = 1; i < numfiles; i++)
                    {
                        sourcefile = files[i].FullName;
                        desfile = Path.Combine(backupPath, files[i].Name);
                        if (File.Exists(desfile))
                            File.Delete(desfile);
                        File.Move(sourcefile, desfile);
                    }
                }
                catch
                {
                    return result;
                }
            }

            return result;
        }

        public class FileInfoComparer : IComparer<FileInfo>
        {
            public int Compare(FileInfo x, FileInfo y)
            {
                return (y.LastWriteTime.CompareTo(x.LastWriteTime));
            }
        }

        private static bool IsOverMaximumSize(FileStream fileStream)
        {
            long length = (fileStream.Length / 1024); //KB
            if (length > m_MaximumFileSize) //KB
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private static bool IsOverMaximumSize(string fileName)
        {
            FileStream fs = null;
            try
            {
                fs = File.Open(fileName, FileMode.Append);
            }
            catch
            {
                return true;
            }

            long length = (fs.Length / 1024); //KB
            fs.Close();

            if (length > m_MaximumFileSize) //KB
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private static string GetString(LogLevel level)
        {
            string str;
            if (level == LogLevel.Debug)
            {
                str = "DEBUG";
            }
            else if (level == LogLevel.Info)
            {
                str = "INFO";
            }
            else if (level == LogLevel.Warning)
            {
                str = "WARNING";
            }
            else if (level == LogLevel.Error)
            {
                str = "ERROR";
            }
            else if (level == LogLevel.Fatal)
            {
                str = "FATAL";
            }
            else
            {
                str = string.Empty;
            }

            return str;
        }

        private static string GetPrefix(LogLevel level)
        {
            string result;
            if (level == LogLevel.Debug)
            {
                result = PRE_FIX_DEBUG;
            }
            else if (level == LogLevel.Info)
            {
                result = PRE_FIX_INFO;
            }
            else if (level == LogLevel.Warning)
            {
                result = PRE_FIX_WARNING;
            }
            else if (level == LogLevel.Error)
            {
                result = PRE_FIX_ERROR;
            }
            else if (level == LogLevel.Fatal)
            {
                result = PRE_FIX_FATAL;
            }
            else
            {
                result = string.Empty;
            }

            return result;
        }

        private static string GetCurrentDateTime()
        {
            string pattern = "MM-dd-yyyy H:mm:ss";
            string timevalue = DateTime.Now.ToString(pattern, CultureInfo.CreateSpecificCulture("en-US"));
            timevalue = timevalue.Replace("-", "_");
            timevalue = timevalue.Replace(":", "_");

            return timevalue;
        }

        private static string GetNextFileLog(LogLevel level)
        {
            string time;
            string filename = string.Empty;

            time = GetCurrentDateTime();
            filename = Path.Combine(m_LogPath, string.Format("{0}_{1}.log", GetPrefix(level), time));

            return filename;
        }

        private static string GetCurrentFileLog(LogLevel level)
        {
            string result;
            if (level == LogLevel.Debug)
            {
                result = m_logfile_Debug;
            }
            else if (level == LogLevel.Info)
            {
                result = m_logfile_Info;
            }
            else if (level == LogLevel.Warning)
            {
                result = m_logfile_Warning;
            }
            else if (level == LogLevel.Error)
            {
                result = m_logfile_Error;
            }
            else if (level == LogLevel.Fatal)
            {
                result = m_logfile_Fatal;
            }
            else
            {
                result = null;
            }

            return result;
        }

        private static void SetCurrentFileLog(LogLevel level, string filename)
        {

            if (level == LogLevel.Debug)
            {
                m_logfile_Debug = filename;
            }
            else if (level == LogLevel.Info)
            {
                m_logfile_Info = filename;
            }
            else if (level == LogLevel.Warning)
            {
                m_logfile_Warning = filename;
            }
            else if (level == LogLevel.Error)
            {
                m_logfile_Error = filename;
            }
            else if (level == LogLevel.Fatal)
            {
                m_logfile_Fatal = filename;
            }
        }

        private static void WriteLog(LogLevel level, object message)
        {
            if (m_isLog[0] == true)
                return;

            string m_logfile = GetCurrentFileLog(level);
            if (string.IsNullOrEmpty(m_logfile))
            {
                m_logfile = GetNextFileLog(level);
                if (!string.IsNullOrEmpty(m_logfile))
                {
                    SetCurrentFileLog(level, m_logfile);
                }
                else
                {
                    return;
                }
            }

            FileStream fs = null;
            try
            {
                fs = File.Open(m_logfile, FileMode.Append);
            }
            catch
            {
                return;
            }

            if (IsOverMaximumSize(fs) == true)
            {
                fs.Close();
                m_logfile = GetNextFileLog(level);
                if (!string.IsNullOrEmpty(m_logfile))
                {
                    SetCurrentFileLog(level, m_logfile);
                }

                try
                {
                    fs = File.Open(m_logfile, FileMode.Append);
                }
                catch
                {
                    return;
                }
            }

            lock (fs)
            {
                StreamWriter sw = new StreamWriter(fs);

                string preText = string.Format("{0} : [{1}]", DateTime.Now, GetString(level));

                //begin to write log
                if (message is Exception)
                {
                    Exception e = message as Exception;
                    if (m_isLog[1] == true)
                    {
                        sw.WriteLine(preText + e.StackTrace);
                    }
                    else
                    {
                        sw.WriteLine(preText + e.Message);
                    }
                }
                else
                {
                    sw.WriteLine(preText + message);
                }
                sw.Flush();
                sw.Close();
                fs.Close();
            }
        }

        private static void WriteLog(LogLevel level, object message, Exception exception)
        {
            if (m_isLog[0] == true)
                return;

            string m_logfile = GetCurrentFileLog(level);
            if (string.IsNullOrEmpty(m_logfile))
            {
                m_logfile = GetNextFileLog(level);
                if (!string.IsNullOrEmpty(m_logfile))
                {
                    SetCurrentFileLog(level, m_logfile);
                }
                else
                {
                    return;
                }
            }

            FileStream fs = null;
            try
            {
                fs = File.Open(m_logfile, FileMode.Append);
            }
            catch
            {
                return;
            }

            if (IsOverMaximumSize(fs) == true)
            {
                fs.Close();
                m_logfile = GetNextFileLog(level);
                if (!string.IsNullOrEmpty(m_logfile))
                {
                    SetCurrentFileLog(level, m_logfile);
                }

                try
                {
                    fs = File.Open(m_logfile, FileMode.Append);
                }
                catch
                {
                    return;
                }
            }

            lock (fs)
            {
                StreamWriter sw = new StreamWriter(fs);

                string preText = string.Format("{0} : [{1}]", DateTime.Now, GetString(level));

                //begin to write log
                if (message is Exception)
                {
                    Exception e = message as Exception;
                    if (m_isLog[1] == true)
                    {
                        sw.WriteLine(preText + e.Message + "\n" + e.InnerException + "\n" + e.StackTrace);
                    }
                    else
                    {
                        sw.WriteLine(preText + e.Message + "\n" + e.InnerException);
                    }
                }
                else
                {
                    sw.WriteLine(preText + message);
                }

                if (exception != null)
                {
                    sw.WriteLine("Exceptions:" + exception.Message);
                    sw.WriteLine(exception.Source);
                    sw.WriteLine(exception.StackTrace);
                }

                sw.Flush();
                sw.Close();
                fs.Close();
            }
        }

        #endregion

        #region Old APIs
        public static string LogPath
        {
            get
            {
                return m_LogPath;
            }
            set { m_LogPath = value; }
        }

        /// <summary>
        /// Write a message to error log file
        /// </summary>
        /// <param name="message">Message to write</param>
        public static void WriteError(Exception ex)
        {
            StreamWriter sw = new StreamWriter(Path.Combine(LogPath, "Error.Log"), true);
            sw.WriteLine(string.Format("{0}: {1}", DateTime.Now, ex.StackTrace));
            sw.Flush();
            sw.Close();
            sw = null;
        }
        /// <summary>
        /// Write a message to error log file
        /// </summary>
        /// <param name="message">Message to write</param>
        public static void WriteError(string message)
        {
            StreamWriter sw = new StreamWriter(Path.Combine(LogPath, "Error.Log"), true);
            sw.WriteLine(string.Format("{0}: {1}", DateTime.Now, message));
            sw.Flush();
            sw.Close();
            sw = null;
        }

        /// <summary>
        /// Write debug info
        /// </summary>
        /// <param name="message">Message to debug</param>
        public static void WriteDebug(string message)
        {
            if (IsDebugEnabled)
            {
                if (!Directory.Exists(m_LogPath))
                    Directory.CreateDirectory(m_LogPath);
                StreamWriter sw = new StreamWriter(Path.Combine(LogPath, "Debug.Log"), true);
                sw.WriteLine(string.Format("{0}: {1}", DateTime.Now, message));
                sw.Flush();
                sw.Close();
                sw = null;
            }
        }
        #endregion
    }
}
